# import magrittr for piping
library(magrittr)
library(readr)
library(dplyr)
library(plotly)
#read in data to analyze
atp_table <- read_csv("atp_tennis.csv")
Rows: 25362 Columns: 17── Column specification ──────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr  (9): Tournament, Series, Court, Surface, Round, Player_1, Player_2, Winner, score
dbl  (7): Best of, Rank_1, Rank_2, Pts_1, Pts_2, Odd_1, Odd_2
date (1): Date
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
head(atp_table)
NA
library(ggplot2)
library(plotly)
time_data <- c(15/60,15/60,20/60,10/60,30/60,30/60,
               10/60,5,3.5,7.75,5.5,7.5)
date_data <- c("8/29/2023", "9/7/2023",
               "8/29/2023", "9/3/2023",
               "9/17/2023", "9/24/2023",
               "9/10/2023", "9/24/2023",
               "10/1/2023", "10/8/2023",
               "10/9/2023", "10/10/2023")
duty <- c("starting Github repository",
          "starting Github repository",
          "Selecting Dataset",
          "Selecting Dataset",
          "Describing Dataset",
          "Describing Dataset",
          "Starting R file",
          paste("Making Shiny App",
            "histogram", "word cloud", sep = "\n"),
          paste("Making Shiny App", "line graph"),
          paste("Making Shiny App", "network", 
            sep = "\n"),
          paste("Making Shiny App", "network",
            "map testing", "documentation", 
            sep = "\n"),
          paste("Making Shiny App", "network",
            "extra data", "documentation",
            sep = "\n"))
date_data %<>% as.Date(format="%m/%d/%Y")
time_log_data <- data.frame(time_data, date_data, duty)
time_log_data <- time_log_data[order(time_log_data$date_data),]
p <- ggplot(time_log_data, aes(y=time_data, x=date_data, label = duty))+
  geom_bar(stat = "identity", fill = "lightgreen")+
  xlab("Date")+
  ylab("Hrs Spent") + 
  scale_x_date(date_breaks = "7 day")+
  scale_x_date(date_minor_breaks = "1 day")
Scale for x is already present.
Adding another scale for x, which will replace the existing scale.
ggplotly(p)
#plotly tests
atp_dated <- atp_table %>% filter(Date < "2013-01-01")
plot_ly(atp_dated, x = ~Rank_1, y = ~Rank_2, 
        text = ~Player_1, type = 'scatter', mode = 'markers',
        marker = list(size = ~Odd_2, opacity = 0.5))
player_name = "Djokovic N."
atp_table_swapped <- atp_table %>% rename(Player_1 = Player_2, Player_2 = Player_1)
atp_combined <- rbind(atp_table, atp_table_swapped)
atp_combined <- atp_combined[order(atp_combined$Date),]
opponents <- atp_combined %>%
  filter(Player_1 == player_name) %>%
  select(Player_2) %>%
  table(dnn = "name")
opponents %<>% as.data.frame()
opponents <- opponents[rev(order(opponents$Freq)),]
fig <- plot_ly(opponents, x = ~name, y = ~Freq, type = 'bar')
fig
player_results = list()
players = c("Djokovic N.", "Nadal R.")
for(player in players){
  # grab the relevant player ratings
  atp_player_result <- atp_combined %>%
    filter(Player_1 == player) %>%
    # grab names ranks and dates
    select(Rank_1 | Rank_2 | Date | Player_1 | Player_2)
  # add the player result to the list
  player_results[[length(player_results)+1]] = atp_player_result
}
ggplot(bind_rows(player_results, .id="data_frame"),
       aes(x=Date, y=Rank_1, group = Player_1)) +
  geom_line(aes(color = Player_1)) +
  scale_y_reverse() +
  labs(y = "Ranking", title = "Player Ranking over Time")

library(ggplot2)

ggplot(head(opponents,10), aes(x=name, y = Freq))
library(shiny)
player_selections <<- data.frame( player = 
                                    c(atp_table$Player_1, 
                                      atp_table$Player_2)) %>%
  # used https://www.rdocumentation.org/packages/base/versions/3.6.2/topics/table
  # need the column name to be the same every time
  table(dnn = list("name")) %>%
  as.data.frame(responseName = "freq")

# https://stackoverflow.com/questions/62716572/selectinput-category-selection
# Stéphane Laurent
# https://shiny.posit.co/r/reference/shiny/latest/updateselectinput
# for context (lets me know onInitialize is running as JavaScript)
onInitialize <- "
function(){
  var select = this.$input[0];
  this.$dropdown_content.on('mousedown', function(e){
    e.preventDefault(); 
    return false;
  }).on('click', '.optgroup-header', function(e){
    var options = $(this).parent().find('.option');
    var items = [];
    options.each(function(i, opt){items.push($(opt).data('value'));});
    var selections = select.selectize.items;
    select.selectize.setValue(items.concat(selections));
  });
}
"

shinyApp(
  ui = fluidPage(
    selectizeInput("state", "Choose a player:",
                   player_selections,
                   multiple = TRUE, 
                   options = list(
                     onInitialize = I(onInitialize)
                   )
    )
  ),
  server = function(input, output){}
)
library(GGally)
library(igraph)
player = "Djokovic N." 
    atp_player_result <- atp_combined %>%
      filter(Player_1 == player) %>%
      # grab names ranks and dates
      select(Player_2)
    #make a data frame with the frequency of occurrences
    network_table_df <- atp_player_result %>%
      table(dnn = list("name")) %>%
      as.data.frame(responseName = "freq")
    #order by frequency
    network_table_df <- network_table_df[order(network_table_df$freq, decreasing = TRUE),]
    simple_edge_df <- data.frame(
      from = rep(player,length(network_table_df$name)),
      to = network_table_df$name,
      weight = network_table_df$freq
    )
    
    net <- graph_from_data_frame(simple_edge_df)
    p <- ggnet2(simplify(net), label = TRUE)
    ggplotly(p) %>% layout(xaxis = list(visible = FALSE),
                           yaxis = list(visible = FALSE))
simplify(net)
rep(player,length(atp_player_result$Player_2))

lets see if we can make

#read in data to analyze
old_atp_df <- read_csv("Tennis Data.csv")
head(old_atp_df)
library(dplyr)
#old_tournament_locations <- old_atp_df[!duplicated(old_atp_df$Tournament),]
new_tournaments <- atp_table[!duplicated(atp_table$Tournament), "Tournament"]
#new_tournaments$Tournament %in%old_tournament_locations$Tournament
#old_tournament_locations$Tournament %in% new_tournaments$Tournament
new_tournaments
library(maps)
#map.cities(old_tournament_locations$Location)
world.cities
#old_tournament_locations$Location[which(old_tournament_locations$Location %in% world.cities$name)]

world.cities[which(world.cities$name %in% old_tournament_locations$Location),]
locations <- world.cities[which(world.cities$name %in% old_tournament_locations$Location),]
fig <- plot_geo(locations, lat = ~lat, lon = ~long)

fig <- fig %>% add_markers(

    text = ~paste(airport, city, state, paste("Arrivals:", cnt), sep = "<br />"),

    color = ~cnt, symbol = I("square"), size = I(8), hoverinfo = "text"

  )
fig

result <- merge(old_tournament_locations,
      world.cities, by.x = "Location", by.y = "name") 

#with(world.data, lat[match(old_tournament_locations$Location, name])
player = "Djokovic N."
tournament = atp_combined$Tournament[1]
atp_player_result <- atp_combined %>%
      filter(Player_1 == player & Tournament == tournament) %>%
      select(Player_2)
atp_player_result

network_table_df <- atp_player_result %>%
      table(dnn = list("name")) %>%
      as.data.frame(responseName = "freq")
    network_table_df <- network_table_df[order(network_table_df$freq, decreasing = TRUE),]
    
edge_df <- data.frame(
      from = rep(player,length(network_table_df$name)),
      to = network_table_df$name,
      weight = network_table_df$freq
    )

net <- graph_from_data_frame(edge_df)
    p <- ggnet2(simplify(net), size = 3, label = TRUE)
    p <- ggplotly(p) %>% layout(xaxis = list(visible = FALSE),
                                yaxis = list(visible = FALSE))
    p
    

old_atp_df$
LS0tDQp0aXRsZTogIkluZGl2aWR1YWwgUHJvamVjdCINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KYXV0aG9yOiBTcGVuY2VyIFNraWRtb3JlDQotLS0NCg0KDQoNCmBgYHtyfQ0KIyBpbXBvcnQgbWFncml0dHIgZm9yIHBpcGluZw0KbGlicmFyeShtYWdyaXR0cikNCmxpYnJhcnkocmVhZHIpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShwbG90bHkpDQojcmVhZCBpbiBkYXRhIHRvIGFuYWx5emUNCmF0cF90YWJsZSA8LSByZWFkX2NzdigiYXRwX3Rlbm5pcy5jc3YiKQ0KaGVhZChhdHBfdGFibGUpDQoNCmBgYA0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KHBsb3RseSkNCnRpbWVfZGF0YSA8LSBjKDE1LzYwLDE1LzYwLDIwLzYwLDEwLzYwLDMwLzYwLDMwLzYwLA0KICAgICAgICAgICAgICAgMTAvNjAsNSwzLjUsNy43NSw1LjUsNy41KQ0KZGF0ZV9kYXRhIDwtIGMoIjgvMjkvMjAyMyIsICI5LzcvMjAyMyIsDQogICAgICAgICAgICAgICAiOC8yOS8yMDIzIiwgIjkvMy8yMDIzIiwNCiAgICAgICAgICAgICAgICI5LzE3LzIwMjMiLCAiOS8yNC8yMDIzIiwNCiAgICAgICAgICAgICAgICI5LzEwLzIwMjMiLCAiOS8yNC8yMDIzIiwNCiAgICAgICAgICAgICAgICIxMC8xLzIwMjMiLCAiMTAvOC8yMDIzIiwNCiAgICAgICAgICAgICAgICIxMC85LzIwMjMiLCAiMTAvMTAvMjAyMyIpDQpkdXR5IDwtIGMoInN0YXJ0aW5nIEdpdGh1YiByZXBvc2l0b3J5IiwNCiAgICAgICAgICAic3RhcnRpbmcgR2l0aHViIHJlcG9zaXRvcnkiLA0KICAgICAgICAgICJTZWxlY3RpbmcgRGF0YXNldCIsDQogICAgICAgICAgIlNlbGVjdGluZyBEYXRhc2V0IiwNCiAgICAgICAgICAiRGVzY3JpYmluZyBEYXRhc2V0IiwNCiAgICAgICAgICAiRGVzY3JpYmluZyBEYXRhc2V0IiwNCiAgICAgICAgICAiU3RhcnRpbmcgUiBmaWxlIiwNCiAgICAgICAgICBwYXN0ZSgiTWFraW5nIFNoaW55IEFwcCIsDQogICAgICAgICAgICAiaGlzdG9ncmFtIiwgIndvcmQgY2xvdWQiLCBzZXAgPSAiXG4iKSwNCiAgICAgICAgICBwYXN0ZSgiTWFraW5nIFNoaW55IEFwcCIsICJsaW5lIGdyYXBoIiksDQogICAgICAgICAgcGFzdGUoIk1ha2luZyBTaGlueSBBcHAiLCAibmV0d29yayIsIA0KICAgICAgICAgICAgc2VwID0gIlxuIiksDQogICAgICAgICAgcGFzdGUoIk1ha2luZyBTaGlueSBBcHAiLCAibmV0d29yayIsDQogICAgICAgICAgICAibWFwIHRlc3RpbmciLCAiZG9jdW1lbnRhdGlvbiIsIA0KICAgICAgICAgICAgc2VwID0gIlxuIiksDQogICAgICAgICAgcGFzdGUoIk1ha2luZyBTaGlueSBBcHAiLCAibmV0d29yayIsDQogICAgICAgICAgICAiZXh0cmEgZGF0YSIsICJkb2N1bWVudGF0aW9uIiwNCiAgICAgICAgICAgIHNlcCA9ICJcbiIpKQ0KZGF0ZV9kYXRhICU8PiUgYXMuRGF0ZShmb3JtYXQ9IiVtLyVkLyVZIikNCnRpbWVfbG9nX2RhdGEgPC0gZGF0YS5mcmFtZSh0aW1lX2RhdGEsIGRhdGVfZGF0YSwgZHV0eSkNCnRpbWVfbG9nX2RhdGEgPC0gdGltZV9sb2dfZGF0YVtvcmRlcih0aW1lX2xvZ19kYXRhJGRhdGVfZGF0YSksXQ0KcCA8LSBnZ3Bsb3QodGltZV9sb2dfZGF0YSwgYWVzKHk9dGltZV9kYXRhLCB4PWRhdGVfZGF0YSwgbGFiZWwgPSBkdXR5KSkrDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gImxpZ2h0Z3JlZW4iKSsNCiAgeGxhYigiRGF0ZSIpKw0KICB5bGFiKCJIcnMgU3BlbnQiKSArIA0KICBzY2FsZV94X2RhdGUoZGF0ZV9icmVha3MgPSAiNyBkYXkiKSsNCiAgc2NhbGVfeF9kYXRlKGRhdGVfbWlub3JfYnJlYWtzID0gIjEgZGF5IikNCmdncGxvdGx5KHApDQpgYGANCg0KYGBge3J9DQojcGxvdGx5IHRlc3RzDQphdHBfZGF0ZWQgPC0gYXRwX3RhYmxlICU+JSBmaWx0ZXIoRGF0ZSA8ICIyMDEzLTAxLTAxIikNCnBsb3RfbHkoYXRwX2RhdGVkLCB4ID0gflJhbmtfMSwgeSA9IH5SYW5rXzIsIA0KICAgICAgICB0ZXh0ID0gflBsYXllcl8xLCB0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ21hcmtlcnMnLA0KICAgICAgICBtYXJrZXIgPSBsaXN0KHNpemUgPSB+T2RkXzIsIG9wYWNpdHkgPSAwLjUpKQ0KYGBgDQoNCmBgYHtyfQ0KcGxheWVyX25hbWUgPSAiRGpva292aWMgTi4iDQphdHBfdGFibGVfc3dhcHBlZCA8LSBhdHBfdGFibGUgJT4lIHJlbmFtZShQbGF5ZXJfMSA9IFBsYXllcl8yLCBQbGF5ZXJfMiA9IFBsYXllcl8xKQ0KYXRwX2NvbWJpbmVkIDwtIHJiaW5kKGF0cF90YWJsZSwgYXRwX3RhYmxlX3N3YXBwZWQpDQphdHBfY29tYmluZWQgPC0gYXRwX2NvbWJpbmVkW29yZGVyKGF0cF9jb21iaW5lZCREYXRlKSxdDQpvcHBvbmVudHMgPC0gYXRwX2NvbWJpbmVkICU+JQ0KICBmaWx0ZXIoUGxheWVyXzEgPT0gcGxheWVyX25hbWUpICU+JQ0KICBzZWxlY3QoUGxheWVyXzIpICU+JQ0KICB0YWJsZShkbm4gPSAibmFtZSIpDQpvcHBvbmVudHMgJTw+JSBhcy5kYXRhLmZyYW1lKCkNCm9wcG9uZW50cyA8LSBvcHBvbmVudHNbcmV2KG9yZGVyKG9wcG9uZW50cyRGcmVxKSksXQ0KZmlnIDwtIHBsb3RfbHkob3Bwb25lbnRzLCB4ID0gfm5hbWUsIHkgPSB+RnJlcSwgdHlwZSA9ICdiYXInKQ0KZmlnDQpgYGANCmBgYHtyfQ0KcGxheWVyX3Jlc3VsdHMgPSBsaXN0KCkNCnBsYXllcnMgPSBjKCJEam9rb3ZpYyBOLiIsICJOYWRhbCBSLiIpDQpmb3IocGxheWVyIGluIHBsYXllcnMpew0KICAjIGdyYWIgdGhlIHJlbGV2YW50IHBsYXllciByYXRpbmdzDQogIGF0cF9wbGF5ZXJfcmVzdWx0IDwtIGF0cF9jb21iaW5lZCAlPiUNCiAgICBmaWx0ZXIoUGxheWVyXzEgPT0gcGxheWVyKSAlPiUNCiAgICAjIGdyYWIgbmFtZXMgcmFua3MgYW5kIGRhdGVzDQogICAgc2VsZWN0KFJhbmtfMSB8IFJhbmtfMiB8IERhdGUgfCBQbGF5ZXJfMSB8IFBsYXllcl8yKQ0KICAjIGFkZCB0aGUgcGxheWVyIHJlc3VsdCB0byB0aGUgbGlzdA0KICBwbGF5ZXJfcmVzdWx0c1tbbGVuZ3RoKHBsYXllcl9yZXN1bHRzKSsxXV0gPSBhdHBfcGxheWVyX3Jlc3VsdA0KfQ0KZ2dwbG90KGJpbmRfcm93cyhwbGF5ZXJfcmVzdWx0cywgLmlkPSJkYXRhX2ZyYW1lIiksDQogICAgICAgYWVzKHg9RGF0ZSwgeT1SYW5rXzEsIGdyb3VwID0gUGxheWVyXzEpKSArDQogIGdlb21fbGluZShhZXMoY29sb3IgPSBQbGF5ZXJfMSkpICsNCiAgc2NhbGVfeV9yZXZlcnNlKCkgKw0KICBsYWJzKHkgPSAiUmFua2luZyIsIHRpdGxlID0gIlBsYXllciBSYW5raW5nIG92ZXIgVGltZSIpDQoNCmBgYA0KDQpgYGB7cn0NCg0KbGlicmFyeShnZ3Bsb3QyKQ0KDQpnZ3Bsb3QoaGVhZChvcHBvbmVudHMsMTApLCBhZXMoeD1uYW1lLCB5ID0gRnJlcSkpDQpgYGANCg0KYGBge3J9DQpsaWJyYXJ5KHNoaW55KQ0KcGxheWVyX3NlbGVjdGlvbnMgPDwtIGRhdGEuZnJhbWUoIHBsYXllciA9IA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYyhhdHBfdGFibGUkUGxheWVyXzEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhdHBfdGFibGUkUGxheWVyXzIpKSAlPiUNCiAgIyB1c2VkIGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy9iYXNlL3ZlcnNpb25zLzMuNi4yL3RvcGljcy90YWJsZQ0KICAjIG5lZWQgdGhlIGNvbHVtbiBuYW1lIHRvIGJlIHRoZSBzYW1lIGV2ZXJ5IHRpbWUNCiAgdGFibGUoZG5uID0gbGlzdCgibmFtZSIpKSAlPiUNCiAgYXMuZGF0YS5mcmFtZShyZXNwb25zZU5hbWUgPSAiZnJlcSIpDQoNCiMgaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvNjI3MTY1NzIvc2VsZWN0aW5wdXQtY2F0ZWdvcnktc2VsZWN0aW9uDQojIFN0w6lwaGFuZSBMYXVyZW50DQojIGh0dHBzOi8vc2hpbnkucG9zaXQuY28vci9yZWZlcmVuY2Uvc2hpbnkvbGF0ZXN0L3VwZGF0ZXNlbGVjdGlucHV0DQojIGZvciBjb250ZXh0IChsZXRzIG1lIGtub3cgb25Jbml0aWFsaXplIGlzIHJ1bm5pbmcgYXMgSmF2YVNjcmlwdCkNCm9uSW5pdGlhbGl6ZSA8LSAiDQpmdW5jdGlvbigpew0KICB2YXIgc2VsZWN0ID0gdGhpcy4kaW5wdXRbMF07DQogIHRoaXMuJGRyb3Bkb3duX2NvbnRlbnQub24oJ21vdXNlZG93bicsIGZ1bmN0aW9uKGUpew0KICAgIGUucHJldmVudERlZmF1bHQoKTsgDQogICAgcmV0dXJuIGZhbHNlOw0KICB9KS5vbignY2xpY2snLCAnLm9wdGdyb3VwLWhlYWRlcicsIGZ1bmN0aW9uKGUpew0KICAgIHZhciBvcHRpb25zID0gJCh0aGlzKS5wYXJlbnQoKS5maW5kKCcub3B0aW9uJyk7DQogICAgdmFyIGl0ZW1zID0gW107DQogICAgb3B0aW9ucy5lYWNoKGZ1bmN0aW9uKGksIG9wdCl7aXRlbXMucHVzaCgkKG9wdCkuZGF0YSgndmFsdWUnKSk7fSk7DQogICAgdmFyIHNlbGVjdGlvbnMgPSBzZWxlY3Quc2VsZWN0aXplLml0ZW1zOw0KICAgIHNlbGVjdC5zZWxlY3RpemUuc2V0VmFsdWUoaXRlbXMuY29uY2F0KHNlbGVjdGlvbnMpKTsNCiAgfSk7DQp9DQoiDQoNCnNoaW55QXBwKA0KICB1aSA9IGZsdWlkUGFnZSgNCiAgICBzZWxlY3RpemVJbnB1dCgic3RhdGUiLCAiQ2hvb3NlIGEgcGxheWVyOiIsDQogICAgICAgICAgICAgICAgICAgcGxheWVyX3NlbGVjdGlvbnMsDQogICAgICAgICAgICAgICAgICAgbXVsdGlwbGUgPSBUUlVFLCANCiAgICAgICAgICAgICAgICAgICBvcHRpb25zID0gbGlzdCgNCiAgICAgICAgICAgICAgICAgICAgIG9uSW5pdGlhbGl6ZSA9IEkob25Jbml0aWFsaXplKQ0KICAgICAgICAgICAgICAgICAgICkNCiAgICApDQogICksDQogIHNlcnZlciA9IGZ1bmN0aW9uKGlucHV0LCBvdXRwdXQpe30NCikNCmBgYA0KDQpgYGB7cn0NCmxpYnJhcnkoR0dhbGx5KQ0KbGlicmFyeShpZ3JhcGgpDQpwbGF5ZXIgPSAiRGpva292aWMgTi4iIA0KICAgIGF0cF9wbGF5ZXJfcmVzdWx0IDwtIGF0cF9jb21iaW5lZCAlPiUNCiAgICAgIGZpbHRlcihQbGF5ZXJfMSA9PSBwbGF5ZXIpICU+JQ0KICAgICAgIyBncmFiIG5hbWVzIHJhbmtzIGFuZCBkYXRlcw0KICAgICAgc2VsZWN0KFBsYXllcl8yKQ0KICAgICNtYWtlIGEgZGF0YSBmcmFtZSB3aXRoIHRoZSBmcmVxdWVuY3kgb2Ygb2NjdXJyZW5jZXMNCiAgICBuZXR3b3JrX3RhYmxlX2RmIDwtIGF0cF9wbGF5ZXJfcmVzdWx0ICU+JQ0KICAgICAgdGFibGUoZG5uID0gbGlzdCgibmFtZSIpKSAlPiUNCiAgICAgIGFzLmRhdGEuZnJhbWUocmVzcG9uc2VOYW1lID0gImZyZXEiKQ0KICAgICNvcmRlciBieSBmcmVxdWVuY3kNCiAgICBuZXR3b3JrX3RhYmxlX2RmIDwtIG5ldHdvcmtfdGFibGVfZGZbb3JkZXIobmV0d29ya190YWJsZV9kZiRmcmVxLCBkZWNyZWFzaW5nID0gVFJVRSksXQ0KICAgIHNpbXBsZV9lZGdlX2RmIDwtIGRhdGEuZnJhbWUoDQogICAgICBmcm9tID0gcmVwKHBsYXllcixsZW5ndGgobmV0d29ya190YWJsZV9kZiRuYW1lKSksDQogICAgICB0byA9IG5ldHdvcmtfdGFibGVfZGYkbmFtZSwNCiAgICAgIHdlaWdodCA9IG5ldHdvcmtfdGFibGVfZGYkZnJlcQ0KICAgICkNCiAgICANCiAgICBuZXQgPC0gZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKHNpbXBsZV9lZGdlX2RmKQ0KICAgIHAgPC0gZ2duZXQyKHNpbXBsaWZ5KG5ldCksIGxhYmVsID0gVFJVRSkNCiAgICBnZ3Bsb3RseShwKSAlPiUgbGF5b3V0KHhheGlzID0gbGlzdCh2aXNpYmxlID0gRkFMU0UpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgeWF4aXMgPSBsaXN0KHZpc2libGUgPSBGQUxTRSkpDQpgYGANCmBgYHtyfQ0Kc2ltcGxpZnkobmV0KQ0KYGBgDQpgYGB7cn0NCnJlcChwbGF5ZXIsbGVuZ3RoKGF0cF9wbGF5ZXJfcmVzdWx0JFBsYXllcl8yKSkNCmBgYA0KbGV0cyBzZWUgaWYgd2UgY2FuIG1ha2UNCmBgYHtyfQ0KI3JlYWQgaW4gZGF0YSB0byBhbmFseXplDQpvbGRfYXRwX2RmIDwtIHJlYWRfY3N2KCJUZW5uaXMgRGF0YS5jc3YiKQ0KaGVhZChvbGRfYXRwX2RmKQ0KYGBgDQpgYGB7cn0NCmxpYnJhcnkoZHBseXIpDQojb2xkX3RvdXJuYW1lbnRfbG9jYXRpb25zIDwtIG9sZF9hdHBfZGZbIWR1cGxpY2F0ZWQob2xkX2F0cF9kZiRUb3VybmFtZW50KSxdDQpuZXdfdG91cm5hbWVudHMgPC0gYXRwX3RhYmxlWyFkdXBsaWNhdGVkKGF0cF90YWJsZSRUb3VybmFtZW50KSwgIlRvdXJuYW1lbnQiXQ0KI25ld190b3VybmFtZW50cyRUb3VybmFtZW50ICVpbiVvbGRfdG91cm5hbWVudF9sb2NhdGlvbnMkVG91cm5hbWVudA0KI29sZF90b3VybmFtZW50X2xvY2F0aW9ucyRUb3VybmFtZW50ICVpbiUgbmV3X3RvdXJuYW1lbnRzJFRvdXJuYW1lbnQNCm5ld190b3VybmFtZW50cw0KYGBgDQpgYGB7cn0NCmxpYnJhcnkobWFwcykNCiNtYXAuY2l0aWVzKG9sZF90b3VybmFtZW50X2xvY2F0aW9ucyRMb2NhdGlvbikNCndvcmxkLmNpdGllcw0KI29sZF90b3VybmFtZW50X2xvY2F0aW9ucyRMb2NhdGlvblt3aGljaChvbGRfdG91cm5hbWVudF9sb2NhdGlvbnMkTG9jYXRpb24gJWluJSB3b3JsZC5jaXRpZXMkbmFtZSldDQoNCndvcmxkLmNpdGllc1t3aGljaCh3b3JsZC5jaXRpZXMkbmFtZSAlaW4lIG9sZF90b3VybmFtZW50X2xvY2F0aW9ucyRMb2NhdGlvbiksXQ0KDQpgYGANCmBgYHtyfQ0KbG9jYXRpb25zIDwtIHdvcmxkLmNpdGllc1t3aGljaCh3b3JsZC5jaXRpZXMkbmFtZSAlaW4lIG9sZF90b3VybmFtZW50X2xvY2F0aW9ucyRMb2NhdGlvbiksXQ0KZmlnIDwtIHBsb3RfZ2VvKGxvY2F0aW9ucywgbGF0ID0gfmxhdCwgbG9uID0gfmxvbmcpDQoNCmZpZyA8LSBmaWcgJT4lIGFkZF9tYXJrZXJzKA0KDQogICAgdGV4dCA9IH5wYXN0ZShhaXJwb3J0LCBjaXR5LCBzdGF0ZSwgcGFzdGUoIkFycml2YWxzOiIsIGNudCksIHNlcCA9ICI8YnIgLz4iKSwNCg0KICAgIGNvbG9yID0gfmNudCwgc3ltYm9sID0gSSgic3F1YXJlIiksIHNpemUgPSBJKDgpLCBob3ZlcmluZm8gPSAidGV4dCINCg0KICApDQpmaWcNCmBgYA0KYGBge3J9DQoNCnJlc3VsdCA8LSBtZXJnZShvbGRfdG91cm5hbWVudF9sb2NhdGlvbnMsDQogICAgICB3b3JsZC5jaXRpZXMsIGJ5LnggPSAiTG9jYXRpb24iLCBieS55ID0gIm5hbWUiKSANCg0KI3dpdGgod29ybGQuZGF0YSwgbGF0W21hdGNoKG9sZF90b3VybmFtZW50X2xvY2F0aW9ucyRMb2NhdGlvbiwgbmFtZV0pDQpgYGANCg0KYGBge3J9DQpwbGF5ZXIgPSAiRGpva292aWMgTi4iDQp0b3VybmFtZW50ID0gYXRwX2NvbWJpbmVkJFRvdXJuYW1lbnRbMV0NCmF0cF9wbGF5ZXJfcmVzdWx0IDwtIGF0cF9jb21iaW5lZCAlPiUNCiAgICAgIGZpbHRlcihQbGF5ZXJfMSA9PSBwbGF5ZXIgJiBUb3VybmFtZW50ID09IHRvdXJuYW1lbnQpICU+JQ0KICAgICAgc2VsZWN0KFBsYXllcl8yKQ0KYXRwX3BsYXllcl9yZXN1bHQNCg0KbmV0d29ya190YWJsZV9kZiA8LSBhdHBfcGxheWVyX3Jlc3VsdCAlPiUNCiAgICAgIHRhYmxlKGRubiA9IGxpc3QoIm5hbWUiKSkgJT4lDQogICAgICBhcy5kYXRhLmZyYW1lKHJlc3BvbnNlTmFtZSA9ICJmcmVxIikNCiAgICBuZXR3b3JrX3RhYmxlX2RmIDwtIG5ldHdvcmtfdGFibGVfZGZbb3JkZXIobmV0d29ya190YWJsZV9kZiRmcmVxLCBkZWNyZWFzaW5nID0gVFJVRSksXQ0KICAgIA0KZWRnZV9kZiA8LSBkYXRhLmZyYW1lKA0KICAgICAgZnJvbSA9IHJlcChwbGF5ZXIsbGVuZ3RoKG5ldHdvcmtfdGFibGVfZGYkbmFtZSkpLA0KICAgICAgdG8gPSBuZXR3b3JrX3RhYmxlX2RmJG5hbWUsDQogICAgICB3ZWlnaHQgPSBuZXR3b3JrX3RhYmxlX2RmJGZyZXENCiAgICApDQoNCm5ldCA8LSBncmFwaF9mcm9tX2RhdGFfZnJhbWUoZWRnZV9kZikNCiAgICBwIDwtIGdnbmV0MihzaW1wbGlmeShuZXQpLCBzaXplID0gMywgbGFiZWwgPSBUUlVFKQ0KICAgIHAgPC0gZ2dwbG90bHkocCkgJT4lIGxheW91dCh4YXhpcyA9IGxpc3QodmlzaWJsZSA9IEZBTFNFKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeWF4aXMgPSBsaXN0KHZpc2libGUgPSBGQUxTRSkpDQogICAgcA0KICAgIA0KYGBgDQpgYGB7cn0NCg0Kb2xkX2F0cF9kZiQNCmBgYA0KDQoNCg==